// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * VM_Class.h
 *
 *  Created on: Jan 1, 2010
 *      Author: dhovemey
 */

#ifndef VM_CLASS_H_
#define VM_CLASS_H_

#include <cassert>
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include "vm/Class.h"
#include "vm/VirtualMachineException.h"
#include "interp/VM_Object.h"
#include "interp/VM_SymbolInfo.h"
#include "interp/VM_Thread.h"
class Class;
class VM_ConstantPoolBuilder;
class VM_FieldAndMethodMapBuilder;
class VM_Interpreter;
class VM_Method;
class VM_Thread;

/**
 * A function used to initialize a VM_Class object representing a
 * magic class (i.e., having magic methods).  Allows object init and cleanup
 * functions to be registered with the VM_Class.
 * This allows class-specific data structures
 * (accessed through the object's magic pointer) to be created
 * and destroyed for each instance of the class.
 */
typedef void (VM_Class_Init_Func)(VM_Class *cls);

/**
 * An object initialization function.
 */
typedef void (VM_Class_Object_Init_Func)(VM_Object *obj, ClassId clsId, uint16_t numFields);

/**
 * An object cleanup function.
 */
typedef void (VM_Class_Object_Cleanup_Func)(VM_Object *obj);

/**
 * A VM_Class is a special VM_Object that represents a class.
 * It is not directly visible to Thud code, but is used
 * internally by the interpreter.
 * In particular, it contains the "realized" constant pool
 * of the VM_Objects instantiated for all constants used
 * by the class.
 */
class VM_Class : public VM_Object {
private:
	friend class VM_ConstantPoolBuilder;

	typedef std::vector<VM_Object *> ConstantList;
	typedef std::map<SymbolId, VM_Method *> MethodMap;
	typedef std::map<SymbolId, int> FieldIndexMap;

	Class *m_cls;
	ConstantList m_constantPool;
	int m_numFields;
	// this is the class id of the class represented by this VM_Class object,
	// not the class id of VM_Class objects in general
	ClassId m_clsIdOfReferencedClass;
	FieldIndexMap m_fieldIndexMap;
	MethodMap m_methodMap;

	// These are used for object initialization/cleanup.
	VM_Class_Object_Init_Func *m_objInitFunc;
	VM_Class_Object_Cleanup_Func *m_objCleanupFunc;

	VM_Class(const VM_Class &);
	VM_Class& operator=(const VM_Class &);

public:
	/**
	 * Constructor.
	 *
	 * @param cls Class object in CodeBase which the VM_Class object
	 *            will be a "wrapper" for
	 */
	VM_Class(Class *cls);

	/** Destructor. */
	~VM_Class();

	/**
	 * Get the Class object.
	 */
	Class *getClass() { return m_cls; }

	/**
	 * Get the number of objects in the realized constant pool.
	 */
	size_t getNumConstants() const { return size_t(m_constantPool.size()); }

	/**
	 * Get the number of fields that instances of this class have.
	 * The returned value includes the special "magic" field,
	 * if any.
	 */
	int getNumFields() const { return m_numFields; }

	/**
	 * Get the index of the first non-magic field in instances of the class.
	 */
	int getFirstNonMagicFieldIndex() const;

	/**
	 * Set the class id of the class referenced by this VM_Class.
	 */
	void setClassIdOfReferencedClass(ClassId clsId) { m_clsIdOfReferencedClass = clsId; }

	/**
	 * Get the class id of the class referenced by this VM_Class.
	 */
	ClassId getClassIdOfReferencedClass() const { return m_clsIdOfReferencedClass; }

	void setObjectInitFunc(VM_Class_Object_Init_Func *objInitFunc) {
		m_objInitFunc = objInitFunc;
	}

	void setObjectCleanupFunc(VM_Class_Object_Cleanup_Func *objCleanupFunc) {
		m_objCleanupFunc = objCleanupFunc;
	}

	void initObject(VM_Object *obj) {
		m_objInitFunc(obj, getClassIdOfReferencedClass(), uint16_t(getNumFields()));
	}

	void cleanupObject(VM_Object *obj) {
		m_objCleanupFunc(obj);
	}

	/**
	 * Resolve a symbol naming a method of the class to the
	 * VM_Method object representing the method.
	 * Throws an exception if this class does not contain a method with that name.
	 *
	 * @param methodNameSym the symbol naming the method
	 * @param thread        the VM_Thread initiating the reference to the method
	 * @return the resolved VM_Method
	 */
	VM_Method *resolveMethod(VM_Object *methodNameSym, VM_Thread *thread) {
		// FIXME: this seems ugly
		if (getNumConstants() == 0) {
			buildConstantPool(thread);
		}

		assert(methodNameSym->getClassId() == CLASS_ID_SYMBOL);
		VM_SymbolInfo *symInfo = (VM_SymbolInfo *) methodNameSym->getMagicPtr();
		MethodMap::iterator i = m_methodMap.find(symInfo->symbolId);
		if (i == m_methodMap.end()) {
			throw VirtualMachineException(L"Call to nonexistent method " +
					m_cls->getClassName() + L"." + symInfo->name);
		}
		return (*i).second;
	}

	/**
	 * Resolve a symbol naming a field of the class to the
	 * offset of the field within instances of the class.
	 * Throws an exception if this class does not contain a field with that name.
	 *
	 * @param methodNameSym the symbol naming the field
	 * @param thread        the VM_Thread initiating the reference to the field
	 * @return the resolved field offset
	 */
	int resolveFieldOffset(VM_Object *fieldNameSym, VM_Thread *thread) {
		// FIXME: this seems ugly
		if (getNumConstants() == 0) {
			buildConstantPool(thread);
		}

		assert(fieldNameSym->getClassId() == CLASS_ID_SYMBOL);
		VM_SymbolInfo *symInfo = (VM_SymbolInfo *) fieldNameSym->getMagicPtr();
		FieldIndexMap::iterator i = m_fieldIndexMap.find(symInfo->symbolId);
		if (i == m_fieldIndexMap.end()) {
			throw VirtualMachineException(L"Reference to nonexistent field " +
					m_cls->getClassName() + L"." + symInfo->name);
		}
		return (*i).second;
	}

	/**
	 * Look up method with given name.
	 *
	 * @param thread the VM_Thread initiating the reference to the method
	 * @param methodName name of the method
	 */
	VM_Method *lookupMethod(VM_Thread *thread, const std::wstring &methodName);

	/**
	 * Force the realized constant pool to be build.
	 * This will instantiate a VM_Object for each constant.
	 *
	 * @param thread the VM_Thread initiating the initialization of the constant pool
	 */
	void buildConstantPool(VM_Thread *thread);

	/**
	 * Get realized VM_Object for given constant in the constant pool.
	 *
	 * @param ref    the index of a constant in the class's constant pool
	 * @param thread the VM_Thread requesting the realized constant
	 */
	VM_Object *getConstant(uint16_t ref, VM_Thread *thread) {
		// FIXME: this seems ugly
		if (m_constantPool.size() == 0) {
			buildConstantPool(thread);
		}

		assert(m_constantPool.size() > 0);
		assert(ref < m_constantPool.size());
		return m_constantPool[ref];
	}

	/**
	 * Mark all VM_Objects reachable from this class
	 * (e.g., all of the realized VM_Objects in the class's constant pool).
	 */
	void markReachableObjects();

	static void defaultObjectInit(VM_Object *obj, ClassId clsId, uint16_t numFields);

private:
	static void defaultObjectCleanup(VM_Object *obj);
};

#endif /* VM_CLASS_H_ */
